/*
 * @(#)CustomerTransactionFacade 1.0 2019/12/24
 *
 * Copyright 2019 JDJR All Rights Reserved.
 * PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 * Author Email: zhangshaobo16@jd.com
 */
package com.bo.mvcframework.v1.servlet;

import com.bo.mvcframework.annotation.BoAutowired;
import com.bo.mvcframework.annotation.BoController;
import com.bo.mvcframework.annotation.BoRequestMapping;
import com.bo.mvcframework.annotation.BoService;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/**
 * 自建servlet类
 *
 * @description: 自建servlet类
 * @author: zhangshaobo16 on 2020/2/27.
 * @version： 1.0.0
 */
public class BoDispatcherServlet extends HttpServlet {
    // 存放controller的map
    private Map<String,Object> mapping = new HashMap<>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            this.doDispatcher(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write("500 Exception [" + e.getMessage() + "]");
        }
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        // 获取web.xml配置的配置文件
        String contextConfigLocation = config.getInitParameter("contextConfigLocation");
        // 获取配置文件内容
        try(InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation)){
            // 加载配置
            Properties configContext = new Properties();
            configContext.load(resourceAsStream);
            // 从配置文件中获取参数
            String scanPackage = configContext.getProperty("scanPackage");
            // 加载所有package下的符合条件的类,加载到map的key中
            doScanner(scanPackage);
            // 加载所有的controller里的方法
            for (String clazzName : mapping.keySet()){
                if (!clazzName.contains(".")) {
                    return;
                }
                // 根据那么获取class\
                Class<?> aClass = Class.forName(clazzName);
                if (aClass.isAnnotationPresent(BoController.class)) {
                    Object instance = aClass.newInstance();
                    // 把class实例放入map中,controller的className与类实例的对应关系
                    mapping.put(clazzName, instance);
                    // 找出controller的基础url
                    String basrUrl = "";
                    if(aClass.isAnnotationPresent(BoRequestMapping.class)){
                        basrUrl = aClass.getAnnotation(BoRequestMapping.class).value();
                    }
                    // 获取controller下所有方法
                    Method[] methods = aClass.getMethods();
                    for (Method method : methods) {
                        // 如果方法是requestMapping方法
                        if (method.isAnnotationPresent(BoRequestMapping.class)) {
                            // 获取方法的url链接
                            String value = method.getAnnotation(BoRequestMapping.class).value();
                            // url做key存入mapping。url与方法的对应关系
                            mapping.put(basrUrl+value, method);
                        }
                    }
                } else if(aClass.isAnnotationPresent(BoService.class)){
                    Object instance = aClass.newInstance();
                    String beanName = aClass.getAnnotation(BoService.class).value();
                    // 类为service,beanName 与 Class实例的对应关系
                    if ("".equals(beanName)) {
                        // 如果beanName没有设置，默认是类名
                        beanName = aClass.getName();
                    }
                    // beanName与类实例的对应
                    mapping.put(beanName, instance);
                    Class<?>[] interfaces = aClass.getInterfaces();
                    for (Class<?> anInterface : interfaces) {
                        // 找到service的所有接口，接口名与实例的对应关系
                        mapping.put(anInterface.getName(), instance);
                    }
                }
            }
            // 都实例化好了，该写依赖注入了
            mapping.values().forEach(instance -> {
                if (instance == null) {
                    return ;
                }
                if (instance.getClass().isAnnotationPresent(BoController.class)) {
                    Field[] fields = instance.getClass().getDeclaredFields();
                    for (Field field : fields) {
                        // 判断是否需要注入
                        if (field.isAnnotationPresent(BoAutowired.class)) {
                            // 待注入的beanName
                            String beanName = null;
                            // 是否制定名称注入
                            String value = field.getAnnotation(BoAutowired.class).value();
                            if (!"".equals(value)) {
                                beanName = value;
                            } else {
                                beanName = field.getType().getName();
                            }
                            field.setAccessible(true);
                            try {
                                field.set(instance, mapping.get(beanName));
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            });
            super.init(config);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }


    }

    private void doScanner(String scanPackage) {
        String name = "/" + scanPackage.replaceAll("\\.", "/");
        URL url = this.getClass().getClassLoader().getResource(name);
        File classDir = new File(url.getFile());
        for (File file : classDir.listFiles()) {
            if(file.isDirectory()){
                doScanner(scanPackage+"."+file.getName());
            }
            if (!file.getName().endsWith(".class")) {
                continue;
            }
            String clazzName = scanPackage + "." + file.getName().replace(".class", "");
            mapping.put(clazzName, null);
        }
    }

    public void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        // 获取请求链接
        String url = req.getRequestURI();
        String contextPath = req.getContextPath();
        url = url.replace(contextPath, "").replaceAll("/+","/");
        // 判断是否有对应的方法
        if (!this.mapping.containsKey(url)) {
            resp.setStatus(400);
            resp.getWriter().write("404 Not Found!");
            return ;
        }
        Method method = (Method)this.mapping.get(url);
        // 获取参数传入方法
        Map<String, String[]> parameterMap = req.getParameterMap();
        // 执行方法
        method.invoke(this.mapping.get(method.getDeclaringClass().getName()), new Object[]{req,resp,parameterMap.get("name")[0]});
    }
}
